#include "SceneManager.h"


SceneManager::SceneManager(){
	buildingShader = false;
	theScene = new Scene();	//initialize the Scene
	render = new Renderer();//initialize the Renderer
	render->scene(theScene);
}


SceneManager::~SceneManager(){
	delete theScene;
	delete render;
}

//Scene functions
void SceneManager::addPlane(vec3 _pos, vec3 _rot, vec3 _up, string _shaderName){
	Shape *thePlane = new Plane(vec4(_pos, 1), _rot, vec4(_up, 1));
	if (_shaderName != ""){
		thePlane->shader(theShaders[_shaderName]);
	}
	theScene->addShape(thePlane);
}
void SceneManager::addSphere(vec3 _pos, vec3 _rot, float _rad, string _shaderName){
	Shape *theSphere = new Sphere(vec4(_pos, 1), _rot, _rad);
	if (_shaderName != ""){
		theSphere->shader(theShaders[_shaderName]);
	}
	theScene->addShape(theSphere);
}
void SceneManager::addTriangle(vec3 _pos, vec3 _rot, vec3 _pt1, vec3 _pt2, vec3 _pt3, vec3 _rotation, string _shaderName){
	Shape *theTri = new Triangle(vec4(_pos, 1), _rot, _pt1, _pt2, _pt3);
	theTri->rotation(_rotation);
	if (_shaderName != ""){
		theTri->shader(theShaders[_shaderName]);
	}
	theScene->addShape(theTri);
}
void SceneManager::addBox(vec3 _pos, vec3 _rot, vec3 _min, vec3 _max, vec3 _rotation, string _shaderName){
	Shape *theBox = new Box(vec4(_pos, 1), _rot, _min, _max);
	theBox->rotation(_rotation);
	if (_shaderName != ""){
		theBox->shader(theShaders[_shaderName]);
	}
	theScene->addShape(theBox);
}
void SceneManager::addOBJ(vec3 _pos, string _path, vec3 _rotation, string _shaderName){
	//create an OBJLoader and load the file
	OBJLoader loader = OBJLoader(theScene);
	//TODO: handle rotation
	loader.setShader(theShaders[_shaderName]);
	loader.loadToScene(_path.c_str());
}
void SceneManager::addLight(vec3 _pos, vec4 _col, float _intense){
	Light *theLight = new Light(_pos, _col, _intense);
	theScene->addLight(theLight);
}

void SceneManager::addCamera(vec3 _pos, vec3 _target, vec3 _up, float _fov, int _width, int _height){
	Camera theCam = Camera(_pos, _width, _height, _fov);
	theCam.lookAt(_pos, _target, _up);
	theScene->addCamera(theCam);
}

void SceneManager::clear(){
	theScene->clearCameras();
	theScene->clearLights();
	theScene->clearShapes();
}

//Renderer functions
void SceneManager::imgSize(int _w, int _h, int _layers){
	render->image(new ImageBuffer(_w, _h, _layers));
}

void SceneManager::setActiveCameraToLatest(){
	render->camera(theScene->latestCamera());
}

void SceneManager::imgLayers(int _layers){
	render->image()->layers(_layers);
}

void SceneManager::startRender(){
	Stats *theStats = Stats::instance();
	clock_t t;
	t = clock();
	if(render->prepare()) 
		render->render();
	t = clock() - t;
	printf("Render took %d clicks (%f seconds).\n", t, ((float)t) / CLOCKS_PER_SEC);
	theStats->toString();
}

//Shader functions
void SceneManager::startShaderCollection(string _name){
	if (_name == "")return;
	buildingShader = true;
	curShader = _name;
	theShaders[curShader] = new ShaderCollection();
	theShaders[curShader]->scene(theScene);
}

void SceneManager::addDiffuseShader(float _amount, vec4 _col){
	if (!buildingShader)return;
	theShaders[curShader]->addShader(new DiffuseShader(_amount, _col));
}

void SceneManager::addSpecularShader(float _amount, vec4 _col, float _shine){
	if (!buildingShader)return;
	theShaders[curShader]->addShader(new SpecularShader(_amount, _col, _shine));
}

void SceneManager::addReflectiveShader(float _amount, vec4 _col){
	if (!buildingShader)return;
	theShaders[curShader]->addShader(new ReflectiveShader(_amount, _col));
}

void SceneManager::addRefractiveShader(float _amount, float _IOR){
	if (!buildingShader)return;
	theShaders[curShader]->addShader(new RefractiveShader(_amount, vec4(0, 0, 0, 0), _IOR));
}

void SceneManager::endShaderCollection(){
	buildingShader = false;
	//add this shader to the scene
	theScene->addShader(*(theShaders[curShader]));
}

void SceneManager::saveLayerToFile(string _path, int _layer){
	if (_path == "" || _layer < 0 || _layer >= render->image()->layers()){
		return;	//it needs both of these things
	}
	
	//fill it with the pixels in the layer
	ImageBuffer* theImage = render->image();
	theImage->activeLayer(_layer);
	unsigned width = theImage->width();
	unsigned height = theImage->height();

	vector<unsigned char> pngImage;
	pngImage.resize(width * height * 4);
	//store the render in a vector that lodepng can read
	for (unsigned y = 0; y < height; y++){
		for (unsigned x = 0; x < width; x++){
			vec4 col = theImage->pixel((int)x, (int)(height - y));
			col *= 255;
			pngImage[4 * width * y + 4 * x + 0] = (int)col.r;
			pngImage[4 * width * y + 4 * x + 1] = (int)col.g;
			pngImage[4 * width * y + 4 * x + 2] = (int)col.b;
			pngImage[4 * width * y + 4 * x + 3] = 255;
		}
	}
	//write the image to disk
	lodepng::encode(_path, pngImage, width, height);
}
